/*
 * SPDX-License-Identifier: GPL-2.0
 * rawnand_base.c for sunxi rawnand base
 *
 * Copyright (C) 2019 Allwinner.
 * 2019.9.19 cuizhikui<cuizhikui@allwinnertech.com>
 *
 *                    eNand
 *             Nand flash driver scan module
 *      Copyright(C), 2008-2009, SoftWinners Microelectronic Co., Ltd.
 *              All Rights Reserved
 */

#include <linux/string.h>
#include "rawnand_base.h"
#include "../nand_boot.h"
#include "../nand_errno.h"
#include "../nand_physic_interface.h"
#include "../nand_secure_storage.h"
#include "controller/ndfc_base.h"
#include "controller/ndfc_ops.h"
#include "controller/ndfc_timings.h"
#include "rawnand.h"
#include "rawnand_boot.h"
#include "rawnand_cfg.h"
#include "rawnand_debug.h"
#include "rawnand_chip.h"
#include "../version.h"
#include "../../nand_osal_uboot.h"
#include <asm/io.h>


extern int nand_open_count;
void *g_nreg_base;

struct _nand_temp_buf ntf = {0};
struct _nand_permanent_data nand_permanent_data = {
    MAGIC_DATA_FOR_PERMANENT_DATA,
    0,
};


/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok 1:find good block 2:old arch -1:fail
 *Note         :
 */
int read_nand_structure(void *phy_arch, unsigned int *good_blk_no)
{
	int i, retry = 3, ret, ret2 = -1;
	unsigned int b, chip = 0;
	unsigned int start_blk = 12, blk_cnt = 20;
	unsigned char oob[64];
	struct _nand_permanent_data *parch;
	struct _nand_physic_op_par npo;

	parch = (struct _nand_permanent_data *)nand_get_temp_buf(64 * 1024);

	for (b = start_blk; b < start_blk + blk_cnt; b++) {
		for (i = 0; i < retry; i++) {
			npo.chip = chip;
			npo.block = b;
			npo.page = i;
			npo.mdata = (u8 *)parch;
			npo.sect_bitmap = g_nctri->nci->sector_cnt_per_page;
			npo.sdata = oob;
			npo.slen = g_nctri->nci->sdata_bytes_per_page;

			ret = g_nctri->nci->nand_physic_read_page(&npo);
			if (oob[0] == 0x00) {
				if (ret >= 0) {
					if ((ret >= 0) && (oob[1] == 0x78) && (oob[2] == 0x69) && (oob[3] == 0x87) && (oob[4] == 0x41) && (oob[5] == 0x52) && (oob[6] == 0x43) && (oob[7] == 0x48)) {
						memcpy(phy_arch, parch, sizeof(struct _nand_permanent_data));
						RAWNAND_DBG("search nand structure %d  %d: get last physic arch ok 0x%x 0x%x!\n", npo.block, npo.page, parch->support_two_plane, parch->support_vertical_interleave);
						ret2 = 0;
						goto search_end;
					}
					if ((ret >= 0) && (oob[1] == 0x50) && (oob[2] == 0x48) && (oob[3] == 0x59) && (oob[4] == 0x41) && (oob[5] == 0x52) && (oob[6] == 0x43) && (oob[7] == 0x48)) {
						memcpy(phy_arch, parch, sizeof(struct _nand_permanent_data));
						RAWNAND_DBG("search nand structure %d  %d: get old physic arch ok 0x%x 0x%x!\n", npo.block, npo.page, parch->support_two_plane, parch->support_vertical_interleave);
						ret2 = 2;
						goto search_end;
					}
				} else {
					RAWNAND_DBG("search nand structure: bad block no physic arch info!\n");
				}
			} else if (oob[0] == 0xff) {
				if ((ret >= 0) && (i == 0)) {
					RAWNAND_DBG("search nand structure: find a good block: %d, but no physic arch info.\n", npo.block);
					ret2 = 1;
					goto search_end;
				} else {
					RAWNAND_DBG("search nand structure: blank block!\n");
				}
			} else {
				RAWNAND_DBG("search nand structure: unkonwn 0x%x!\n", oob[0]);
			}
		}
	}

search_end:

	if (b == (start_blk + blk_cnt)) {
		ret2 = ERR_NO_58;
		*good_blk_no = 0;
	} else {
		*good_blk_no = b;
	}

	nand_free_temp_buf((u8 *)parch, 64 * 1024);

	return ret2;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */
int write_nand_structure(void *phy_arch, unsigned int good_blk_no, unsigned int blk_cnt)
{
	int retry = 3, ret, ret2 = ERR_NO_130;
	unsigned int b, p, ok, chip = 0;
	unsigned char oob[64];
	struct _nand_physic_op_par npo;

	RAWNAND_DBG("write nand structure: write physic arch to blk %d...\n", good_blk_no);

	for (b = good_blk_no; b < good_blk_no + blk_cnt; b++) {
		npo.chip = chip;
		npo.block = b;
		npo.page = 0;
		npo.mdata = (unsigned char *)phy_arch;
		npo.sect_bitmap = g_nctri->nci->sector_cnt_per_page;
		npo.sdata = oob;
		npo.slen = g_nctri->nci->sdata_bytes_per_page;

		ret = g_nctri->nci->nand_physic_erase_block(&npo); //PHY_SimpleErase_CurCH(&nand_op);
		if (ret < 0) {
			RAWNAND_ERR("write nand structure: erase chip %d, block %d error\n", npo.chip, npo.block);
			memset(oob, 0, 64);

			for (p = 0; p < g_nctri->nci->page_cnt_per_blk; p++) {
				npo.page = p;
				ret = g_nctri->nci->nand_physic_write_page(&npo);
				nand_wait_all_rb_ready();
				if (ret < 0) {
					RAWNAND_ERR("write nand structure: mark bad block, write chip %d, block %d, page %d error\n", npo.chip, npo.block, npo.page);
				}
			}
		} else {
			RAWNAND_DBG("write nand structure: erase block %d ok.\n", b);
			memset(oob, 0x88, 64);
			oob[0] = 0x00; //bad block flag
			oob[1] = 0x78; //'N'
			oob[2] = 0x69; //'E'
			oob[3] = 0x87; //'W'
			oob[4] = 0x41; //'A'
			oob[5] = 0x52; //'R'
			oob[6] = 0x43; //'C'
			oob[7] = 0x48; //'H'

			for (ok = 0, p = 0; p < g_nctri->nci->page_cnt_per_blk; p++) {
				npo.page = p;
				ret = g_nctri->nci->nand_physic_write_page(&npo);
				nand_wait_all_rb_ready();
				if (ret != 0) {
					RAWNAND_ERR("write nand structure: write chip %d, block %d, page %d error\n", npo.chip, npo.block, npo.page);
				} else {
					if (p < retry) {
						ok = 1;
					}
				}
			}

			if (ok == 1) {
				ret2 = 0;
				break;
			}
		}
	}

	return ret2;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         : only for erase boot
 */
int set_nand_structure(void *phy_arch)
{
	int ret;
	unsigned good_blk_no = 12;
	void *data;
	//	struct _nand_permanent_data *parch;
	//	struct __RAWNandStorageInfo_t *old_parch;

	data = (void *)nand_get_temp_buf(4096);

	ret = read_nand_structure(data, &good_blk_no);
	if (ret == 1) {
		RAWNAND_DBG("search nand structure: ok arch\n");
	} else if (ret == 2) {
		RAWNAND_DBG("search nand structure: old arch\n");
		//        old_parch =  (struct __NandStorageInfo_t *)data;
		//        parch = (struct _nand_permanent_data *)phy_arch;
		//
		//        parch->magic_data = MAGIC_DATA_FOR_PERMANENT_DATA;
		//        parch->support_two_plane = 0;
		//        parch->support_vertical_interleave = 1;
		//        parch->support_dual_channel = 1;
		//        if(old_parch->PlaneCntPerDie == 2)
		//        {
		//            parch->support_two_plane = 1;
		//        }
	} else if (ret == 0) {
		RAWNAND_DBG("never be here! store nand structure\n");
	} else {
		RAWNAND_ERR("search nand structure: can not find good block: 12~112\n");
		nand_free_temp_buf((u8 *)data, 4096);
		return ret;
	}

	ret = write_nand_structure(phy_arch, good_blk_no, 20);
	if (ret != 0) {
		RAWNAND_ERR("write nand structure fail1\n");
	}

	ret = read_nand_structure(data, &good_blk_no);
	if (ret != 0) {
		RAWNAND_ERR("write nand structure: can not find nand structure: 12~112\n");
	}

	nand_free_temp_buf((u8 *)data, 4096);

	return 0;
}

__u32 rawnand_get_lsb_block_size(void)
{
	__u32 i, count = 0;
	struct nand_chip_info *nci;

	nci = g_nctri->nci;

	for (i = 0; i < nci->page_cnt_per_blk; i++) {
		if (1 == nci->is_lsb_page(i))
			count++;
	}
	return count * (nci->sector_cnt_per_page << 9);
}

__u32 rawnand_get_lsb_pages(void)
{
	__u32 i, count = 0;
	struct nand_chip_info *nci;

	nci = g_nctri->nci;

	for (i = 0; i < nci->page_cnt_per_blk; i++) {
		if (1 == nci->is_lsb_page(i))
			count++;
	}
	return count;
}

__u32 rawnand_get_phy_block_size(void)
{
	struct nand_chip_info *nci;

	nci = g_nctri->nci;
	return nci->page_cnt_per_blk * (nci->sector_cnt_per_page << 9);
}

__u32 rawnand_used_lsb_pages(void)
{
	__u32 i, count = 0;
	struct nand_chip_info *nci;

	nci = g_nctri->nci;

	for (i = 0; i < nci->page_cnt_per_blk; i++) {
		if (1 == nci->is_lsb_page(i))
			count++;
	}

	if (count == nci->page_cnt_per_blk) {
		return 0;
	}
	return 1;
}

u32 rawnand_get_pageno(u32 lsb_page_no)
{
	u32 i, count = 0;
	struct nand_chip_info *nci;

	nci = g_nctri->nci;

	for (i = 0; i < nci->page_cnt_per_blk; i++) {
		if (1 == nci->is_lsb_page(i))
			count++;
		if (count == (lsb_page_no + 1))
			break;
	}
	return i;
}

__u32 rawnand_get_page_cnt_per_block(void)
{
	return g_nctri->nci->page_cnt_per_blk;
}

__u32 rawnand_get_pae_size(void)
{
	return ((g_nctri->nci->sector_cnt_per_page) * 512);
}

__u32 rawnand_get_twoplane_flag(void)
{
	return g_nssi->nsci->two_plane;
}

unsigned int rawnand_get_support_v_interleave_flag(struct nand_super_chip_info *schip)
{
	if (!g_nssi) {
		RAWNAND_INFO("rawnand hw not init\n");
		return 0;
	}

	return g_nssi->support_v_interleave;
}

unsigned int rawnand_get_support_dual_channel(void)
{
	if (!g_nssi) {
		RAWNAND_INFO("rawnand hw not init\n");
		return 0;
	}

	return g_nssi->support_dual_channel;
}

int nand_read_scan_data(unsigned int chip, unsigned int block, unsigned int page, unsigned int bitmap, unsigned char *mbuf, unsigned char *sbuf)
{
	return nand_physic_read_page(chip, block, page, bitmap, mbuf, sbuf);
}
/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */
int rawnand_physic_erase_block(unsigned int chip, unsigned int block)
{
	int ret = 0;
	struct nand_chip_info *nci;
	struct _nand_physic_op_par npo;

	nci = nci_get_from_nsi(g_nsi, chip);

	npo.chip = chip;
	npo.block = block;
	npo.page = 0;
	npo.sect_bitmap = 0;
	npo.mdata = NULL;
	npo.sdata = NULL;
	npo.slen = 0;
	ret = nci->nand_physic_erase_block(&npo);

	return ret;
}
/**
 * rawnand read boot0 page: read boot0 page ndfc cfg special
 */
int rawnand_physic_read_boot0_page(unsigned int chip, unsigned int block, unsigned int page, unsigned int bitmap, unsigned char *mbuf, unsigned char *sbuf)
{
	int ret = 0;
	struct nand_chip_info *nci;
	struct _nand_physic_op_par npo;

	nci = nci_get_from_nsi(g_nsi, chip);

	npo.chip = chip;
	npo.block = block;
	npo.page = page;
	npo.mdata = mbuf;
	npo.sdata = sbuf;
	npo.slen = 64;

	ret = nci->nand_read_boot0_page(nci, &npo);

	return ret;
}
/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */

int rawnand_physic_read_page(unsigned int chip, unsigned int block, unsigned int page, unsigned int bitmap, unsigned char *mbuf, unsigned char *sbuf)
{
	int ret = 0;
	struct nand_chip_info *nci;
	struct _nand_physic_op_par npo;

	nci = nci_get_from_nsi(g_nsi, chip);

	npo.chip = chip;
	npo.block = block;
	npo.page = page;
	/*npo.sect_bitmap = g_nsi->nci->sector_cnt_per_page;*/
	npo.sect_bitmap = bitmap;
	npo.mdata = mbuf;
	npo.sdata = sbuf;
	if (npo.sdata)
		npo.slen = nci->sdata_bytes_per_page;
	else
		npo.slen = 0;

	ret = nci->nand_physic_read_page(&npo);

	return ret;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */
int rawnand_physic_write_page(unsigned int chip, unsigned int block, unsigned int page, unsigned int bitmap, unsigned char *mbuf, unsigned char *sbuf)
{
	int ret = 0;
	struct nand_chip_info *nci;
	struct _nand_physic_op_par npo;

	nci = nci_get_from_nsi(g_nsi, chip);

	npo.chip = chip;
	npo.block = block;
	npo.page = page;
	/*npo.sect_bitmap = g_nsi->nci->sector_cnt_per_page;*/
	npo.sect_bitmap = bitmap;
	npo.mdata = mbuf;
	npo.sdata = sbuf;
	npo.slen = nci->sdata_bytes_per_page;
	ret = nci->nand_physic_write_page(&npo);

	return ret;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */
int rawnand_physic_bad_block_check(unsigned int chip, unsigned int block)
{
	int ret;
	struct nand_chip_info *nci;
	struct _nand_physic_op_par npo;

	npo.chip = chip;
	npo.block = block;
	npo.page = 0;
	npo.sect_bitmap = 0;
	npo.mdata = NULL;
	npo.sdata = NULL;
	nci = nci_get_from_nsi(g_nsi, chip);
	ret = nci->nand_physic_bad_block_check(&npo);

	return ret;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */
int rawnand_physic_bad_block_mark(unsigned int chip, unsigned int block)
{
	int ret;
	struct nand_chip_info *nci;
	struct _nand_physic_op_par npo;

	npo.chip = chip;
	npo.block = block;
	npo.page = 0;
	npo.sect_bitmap = 0;
	npo.mdata = NULL;
	npo.sdata = NULL;
	nci = nci_get_from_nsi(g_nsi, chip);
	ret = nci->nand_physic_bad_block_mark(&npo);

	return ret;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */
int rawnand_physic_erase_super_block(unsigned int chip, unsigned int block)
{
	int ret;
	struct nand_super_chip_info *nsci;
	struct _nand_physic_op_par npo;

	npo.chip = chip;
	npo.block = block;
	npo.page = 0;
	npo.sect_bitmap = 0;
	npo.mdata = NULL;
	npo.sdata = NULL;
	npo.slen = 0;
	nsci = nsci_get_from_nssi(g_nssi, chip);
	ret = nsci->nand_physic_erase_super_block(&npo);

	return ret;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */
int rawnand_physic_read_super_page(unsigned int chip, unsigned int block, unsigned int page, unsigned int bitmap, unsigned char *mbuf, unsigned char *sbuf)
{
	int ret;
	struct nand_super_chip_info *nsci;
	struct _nand_physic_op_par npo;

	nsci = nsci_get_from_nssi(g_nssi, chip);

	npo.chip = chip;
	npo.block = block;
	npo.page = page;
	npo.sect_bitmap = bitmap;
	npo.mdata = mbuf;
	npo.sdata = sbuf;
	npo.slen = nsci->spare_bytes;
	ret = nsci->nand_physic_read_super_page(&npo);

	return ret;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */
int rawnand_physic_write_super_page(unsigned int chip, unsigned int block, unsigned int page, unsigned int bitmap, unsigned char *mbuf, unsigned char *sbuf)
{
	int ret;
	struct nand_super_chip_info *nsci;
	struct _nand_physic_op_par npo;

	nsci = nsci_get_from_nssi(g_nssi, chip);

	npo.chip = chip;
	npo.block = block;
	npo.page = page;
	npo.sect_bitmap = bitmap;
	npo.mdata = mbuf;
	npo.sdata = sbuf;
	npo.slen = nsci->spare_bytes;
	ret = nsci->nand_physic_write_super_page(&npo);

	return ret;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */
int rawnand_physic_super_bad_block_check(unsigned int chip, unsigned int block)
{
	int ret;
	struct nand_super_chip_info *nsci;
	struct _nand_physic_op_par npo;

	nsci = nsci_get_from_nssi(g_nssi, chip);

	npo.chip = chip;
	npo.block = block;
	npo.page = 0;
	npo.sect_bitmap = 0;
	npo.mdata = NULL;
	npo.sdata = NULL;
	npo.slen = 0;
	ret = nsci->nand_physic_super_bad_block_check(&npo);

	return ret;
}

/*****************************************************************************
*Name         :
*Description  :
*Parameter    :
*Return       : 0:ok  -1:fail
*Note         :
*****************************************************************************/
int rawnand_physic_super_bad_block_mark(unsigned int chip, unsigned int block)
{
	int ret;
	struct nand_super_chip_info *nsci;
	struct _nand_physic_op_par npo;

	nsci = nsci_get_from_nssi(g_nssi, chip);

	npo.chip = chip;
	npo.block = block;
	npo.page = 0;
	npo.sect_bitmap = 0;
	npo.mdata = NULL;
	npo.sdata = NULL;
	npo.slen = 0;
	ret = nsci->nand_physic_super_bad_block_mark(&npo);

	return ret;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 **/
int nand_write_data_in_whole_block(unsigned int chip, unsigned int block, unsigned char *mbuf, unsigned int mlen, unsigned char *sbuf, unsigned int slen)
{
	int ret, i, flag;
	struct nand_chip_info *nci;
	struct _nand_physic_op_par npo;
	unsigned char spare[64];

	nci = nci_get_from_nsi(g_nsi, chip);

	npo.chip = chip;
	npo.block = block;
	npo.page = 0;
	npo.sect_bitmap = nci->sector_cnt_per_page;
	npo.mdata = NULL;
	npo.sdata = NULL;
	npo.slen = 0;

	ret = nci->nand_physic_erase_block(&npo);
	if (ret != 0) {
		RAWNAND_ERR("nand_write_data_in block error1 chip:0x%x block:0x%x \n", chip, block);
		return ret;
	}
	nand_wait_all_rb_ready();

	memcpy(spare, sbuf, nci->sdata_bytes_per_page);

	flag = ERR_NO_132;
	for (i = 0; i < nci->page_cnt_per_blk; i++) {
		npo.chip = chip;
		npo.block = block;
		npo.page = i;
		npo.sect_bitmap = nci->sector_cnt_per_page;
		npo.mdata = mbuf;
		npo.sdata = spare;
		npo.slen = nci->sdata_bytes_per_page;
		ret = nci->nand_physic_write_page(&npo);
		nand_wait_all_rb_ready();
		if (ret == 0) {
			flag = 0;
		} else {
			RAWNAND_ERR("nand_write_data_in block error2 chip:0x%x block:0x%x page:0x%x \n", chip, block, i);
		}
	}

	return flag;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */
int nand_read_data_in_whole_block(unsigned int chip, unsigned int block, unsigned char *mbuf, unsigned int mlen, unsigned char *sbuf, unsigned int slen)
{
	int ret, i, flag, page_size;
	struct nand_chip_info *nci;
	struct _nand_physic_op_par npo;
	unsigned char spare[64];
	unsigned char *buf;

	page_size = g_nsi->nci->sector_cnt_per_page << 9;

	if (mlen < page_size) {
		buf = nand_get_temp_buf(page_size);
	} else {
		buf = mbuf;
	}

	nci = nci_get_from_nsi(g_nsi, chip);

	flag = ERR_NO_132;
	for (i = 0; i < nci->page_cnt_per_blk; i++) {
		npo.chip = chip;
		npo.block = block;
		npo.page = i;
		npo.sect_bitmap = nci->sector_cnt_per_page;
		npo.mdata = buf;
		npo.sdata = spare;
		npo.slen = nci->sdata_bytes_per_page;
		ret = nci->nand_physic_read_page(&npo);
		if (ret >= 0) {
			flag = 0;
			break;
		} else {
			RAWNAND_ERR("nand_read_data_in block error2 chip:0x%x block:0x%x page:0x%x \n", chip, block, i);
		}
	}

	memcpy(sbuf, spare, slen);
	if (mlen < page_size) {
		memcpy(mbuf, buf, mlen);
		nand_free_temp_buf(buf, page_size);
	}

	return flag;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */
int rawnand_physic_block_copy(unsigned int chip_s, unsigned int block_s, unsigned int chip_d, unsigned int block_d)
{
	int i, ret = 0;
	unsigned char spare[64];
	unsigned char *buf;

	buf = (unsigned char *)nand_get_temp_buf(g_nsi->nci->sector_cnt_per_page << 9);

	for (i = 0; i < g_nsi->nci->page_cnt_per_blk; i++) {
		ret |= nand_physic_read_page(chip_s, block_s, i, g_nsi->nci->sector_cnt_per_page, buf, spare);
		ret |= nand_physic_write_page(chip_d, block_d, i, g_nsi->nci->sector_cnt_per_page, buf, spare);
	}

	nand_free_temp_buf(buf, g_nsi->nci->sector_cnt_per_page << 9);
	return ret;
}

/*
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 */
int get_nand_structure(struct _nand_super_storage_info *nssi)
{
	int good_block, ret;

	rawnand_storage_info_t *nand_storage;

	nssi->support_two_plane = 1;
	nssi->support_v_interleave = 1;
	nssi->support_dual_channel = 1;

	if (is_phyinfo_empty(phyinfo_buf) != 1) {
		nssi->support_two_plane = phyinfo_buf->storage_info.data.support_two_plane;
		nssi->support_v_interleave = phyinfo_buf->storage_info.data.support_v_interleave;
		nssi->support_dual_channel = phyinfo_buf->storage_info.data.support_dual_channel;
		return 0;
	}

	ret = read_nand_structure((void *)(&nand_permanent_data), (u32 *)(&good_block));
	if ((ret == 0) || (ret == 2)) {
		RAWNAND_DBG("get nand structure ok!\n");
		if (nand_permanent_data.magic_data == MAGIC_DATA_FOR_PERMANENT_DATA) {
			RAWNAND_DBG("get nand structure 1!\n");
			nssi->support_two_plane = nand_permanent_data.support_two_plane;
			nssi->support_v_interleave = nand_permanent_data.support_vertical_interleave;
			nssi->support_dual_channel = nand_permanent_data.support_dual_channel;
		} else {
			nand_storage = (rawnand_storage_info_t *)(&nand_permanent_data);
			if (nand_storage->PlaneCntPerDie == 2) {
				RAWNAND_DBG("get nand structure 2 %d!\n", nand_storage->PlaneCntPerDie);
				nssi->support_two_plane = 1;
				memset((void *)&nand_permanent_data, 0, sizeof(struct _nand_permanent_data));
				nand_permanent_data.magic_data = MAGIC_DATA_FOR_PERMANENT_DATA;
				nand_permanent_data.support_two_plane = 1;
				nand_permanent_data.support_vertical_interleave = 1;
				nand_permanent_data.support_dual_channel = 1;
			} else {
				RAWNAND_DBG("get nand structure 3 0x%x!\n", nand_storage->PlaneCntPerDie);
				memset((void *)&nand_permanent_data, 0, sizeof(struct _nand_permanent_data));
				nand_permanent_data.magic_data = MAGIC_DATA_FOR_PERMANENT_DATA;
				nand_permanent_data.support_two_plane = 0;
				nand_permanent_data.support_vertical_interleave = 1;
				nand_permanent_data.support_dual_channel = 1;
			}
		}
	}
	//    else
	//    {
	//        RAWNAND_ERR("get nand structure fail!\n");
	//        return ERR_NO_131;
	//    }

	phyinfo_buf->storage_info.data.support_two_plane = nssi->support_two_plane;
	phyinfo_buf->storage_info.data.support_v_interleave = nssi->support_v_interleave;
	phyinfo_buf->storage_info.data.support_dual_channel = nssi->support_dual_channel;

	return 0;
}
/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       :
 *Note         :the return pointer cannot be modified! must call by pair!
 *****************************************************************************/
s32 nand_get_dma_desc(struct nand_controller_info *nctri)
{

	if (nctri->ndfc_dma_desc == 0) {
		nctri->ndfc_dma_desc_cpu = NAND_Malloc(4 * 1024);
		nctri->ndfc_dma_desc = nctri->ndfc_dma_desc_cpu;
		if (nctri->ndfc_dma_desc == NULL) {
			RAWNAND_ERR("get_dma_desc fail!\n");
			return -1;
		}
	}
	return 0;
}
/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int init_parameter(void)
{
	int i;
	struct nand_controller_info *nctri;

	g_nsi = NULL;
	g_nssi = NULL;
	g_nctri = NULL;
	g_nand_storage_info = NULL;

	//g_nsi = (struct _nand_storage_info *)NAND_Malloc(sizeof(struct _nand_storage_info));
	g_nsi = &g_nsi_data;
	if (g_nsi == NULL) {
		RAWNAND_ERR("init_parameter, no memory for g_nsi\n");
		return NAND_OP_FALSE;
	}
	memset(g_nsi, 0, sizeof(struct _nand_storage_info));

	//g_nssi = (struct _nand_super_storage_info *)NAND_Malloc(sizeof(struct _nand_super_storage_info));
	g_nssi = &g_nssi_data;
	if (g_nssi == NULL) {
		RAWNAND_ERR("init_parameter, no memory for g_nssi\n");
		return NAND_OP_FALSE;
	}
	memset(g_nssi, 0, sizeof(struct _nand_super_storage_info));

	for (i = 0; i < MAX_CHANNEL; i++) {
		//nctri = (struct nand_controller_info *)NAND_Malloc(sizeof(struct nand_controller_info));
		nctri = &g_nctri_data[i];
		if (nctri == NULL) {
			RAWNAND_ERR("init_parameter, no memory for g_nctri\n");
			return NAND_OP_FALSE;
		}
		/*memset(nctri, 0, sizeof(struct nand_controller_info));*/

		add_to_nctri(nctri);

		if (init_nctri(nctri)) {
			RAWNAND_ERR("nand_physic_init, init nctri error\n");
			return NAND_OP_FALSE;
		}
		nand_get_dma_desc(nctri);
	}

	//g_nand_storage_info = (struct __NandStorageInfo_t *)NAND_Malloc(sizeof(struct __NandStorageInfo_t));
	g_nand_storage_info = &g_nand_storage_info_data;
	if (g_nand_storage_info == NULL) {
		RAWNAND_ERR("init_parameter, no memory for g_nand_storage_info\n");
		return NAND_OP_FALSE;
	}

	//	function_read_page_end = generic_read_page_end_not_retry;

	nand_init_temp_buf(&ntf);

	return NAND_OP_TRUE;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       :
 *Note         :the return pointer cannot be modified! must call by pair!
 *****************************************************************************/
s32 nand_free_dma_desc(struct nand_controller_info *nctri)
{
	//NAND_FreeMemoryForDMADescs(&nctri->ndfc_dma_desc_cpu,&nctri->ndfc_dma_desc);
	NAND_Free(nctri->ndfc_dma_desc, 4 * 1024);
	nctri->ndfc_dma_desc = NULL;
	nctri->ndfc_dma_desc_cpu = NULL;

	return 0;
}
/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int nand_init_temp_buf(struct _nand_temp_buf *nand_temp_buf)
{
	int i;

	memset(nand_temp_buf, 0, sizeof(struct _nand_temp_buf));

	for (i = 0; i < NUM_16K_BUF; i++) {
		nand_temp_buf->nand_temp_buf16k[i] = (u8 *)NAND_Malloc(16384);
		if (nand_temp_buf->nand_temp_buf16k[i] == NULL) {
			RAWNAND_ERR("no memory for nand_init_temp_buf 16K\n");
			return NAND_OP_FALSE;
		}
	}

	for (i = 0; i < NUM_32K_BUF; i++) {
		nand_temp_buf->nand_temp_buf32k[i] = (u8 *)NAND_Malloc(32768);
		if (nand_temp_buf->nand_temp_buf32k[i] == NULL) {
			RAWNAND_ERR("no memory for nand_init_temp_buf 32K\n");
			return NAND_OP_FALSE;
		}
	}

	for (i = 0; i < NUM_64K_BUF; i++) {
		nand_temp_buf->nand_temp_buf64k[i] = (u8 *)NAND_Malloc(65536);
		if (nand_temp_buf->nand_temp_buf64k[i] == NULL) {
			RAWNAND_ERR("no memory for nand_init_temp_buf 64K\n");
			return NAND_OP_FALSE;
		}
	}
	return 0;
}
/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int nand_exit_temp_buf(struct _nand_temp_buf *nand_temp_buf)
{
	int i;

	for (i = 0; i < NUM_16K_BUF; i++) {
		NAND_Free(nand_temp_buf->nand_temp_buf16k[i], 16384);
	}

	for (i = 0; i < NUM_32K_BUF; i++) {
		NAND_Free(nand_temp_buf->nand_temp_buf32k[i], 32768);
	}

	for (i = 0; i < NUM_64K_BUF; i++) {
		NAND_Free(nand_temp_buf->nand_temp_buf64k[i], 65536);
	}
	return 0;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       :
 *Note         :the return pointer cannot be modified! must call by pair!
 *****************************************************************************/
u8 *nand_get_temp_buf(unsigned int size)
{
	unsigned int i;

	if (size <= 16384) {
		for (i = 0; i < NUM_16K_BUF; i++) {
			if (ntf.used_16k[i] == 0) {
				ntf.used_16k[i] = 1;
				return ntf.nand_temp_buf16k[i];
			}
		}
	}

	if (size <= 32768) {
		for (i = 0; i < NUM_32K_BUF; i++) {
			if (ntf.used_32k[i] == 0) {
				ntf.used_32k[i] = 1;
				return ntf.nand_temp_buf32k[i];
			}
		}
	}

	if (size <= 65536) {
		for (i = 0; i < NUM_64K_BUF; i++) {
			if (ntf.used_64k[i] == 0) {
				ntf.used_64k[i] = 1;
				return ntf.nand_temp_buf64k[i];
			}
		}
	}

	for (i = 0; i < NUM_NEW_BUF; i++) {
		if (ntf.used_new[i] == 0) {
			RAWNAND_DBG("get memory :%d. \n", size);
			ntf.used_new[i] = 1;
			ntf.nand_new_buf[i] = (u8 *)NAND_Malloc(size);
			if (ntf.nand_new_buf[i] == NULL) {
				RAWNAND_ERR("%s:malloc fail\n", __func__);
				ntf.used_new[i] = 0;
				return NULL;
			}
			return ntf.nand_new_buf[i];
		}
	}

	RAWNAND_ERR("get memory fail %d. \n", size);
	//while(1);
	return NULL;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int nand_free_temp_buf(unsigned char *buf, unsigned int size)
{
	int i;

	for (i = 0; i < NUM_16K_BUF; i++) {
		if (ntf.nand_temp_buf16k[i] == buf) {
			ntf.used_16k[i] = 0;
			return 0;
		}
	}

	for (i = 0; i < NUM_32K_BUF; i++) {
		if (ntf.nand_temp_buf32k[i] == buf) {
			ntf.used_32k[i] = 0;
			return 0;
		}
	}

	for (i = 0; i < NUM_64K_BUF; i++) {
		if (ntf.nand_temp_buf64k[i] == buf) {
			ntf.used_64k[i] = 0;
			return 0;
		}
	}

	for (i = 0; i < NUM_NEW_BUF; i++) {
		if (ntf.nand_new_buf[i] == buf) {
			ntf.used_new[i] = 0;
			NAND_Free(ntf.nand_new_buf[i], size);
			return 0;
		}
	}

	RAWNAND_ERR("free memory fail %d. \n", size);
	//while(1);
	return -1;
}
/**
 *	rawnand_delete storage info: empety g_nand_storage_info and point null
 */
void rawnand_delete_storage_info(void)
{
	memset(g_nand_storage_info, 0, sizeof(*g_nand_storage_info));
	g_nand_storage_info = NULL;
}
/**
 * rawnand_storage_init: build storage info struction
 */
int rawnand_storage_init(void)
{
	g_nand_storage_info = &g_nand_storage_info_data;
	if (g_nand_storage_info == NULL) {
		RAWNAND_ERR("%s no memory to storage nand info\n", __func__);
		return ERR_NO_12;
	}

	g_nand_storage_info->ChannelCnt = g_nssi->nsci->channel_num;
	g_nand_storage_info->ChipCnt = g_nsi->chip_cnt;
	g_nand_storage_info->ChipConnectInfo = g_nctri->chip_connect_info;
	g_nand_storage_info->RbCnt = g_nsi->chip_cnt;
	g_nand_storage_info->RbConnectInfo = g_nctri->rb_connect_info;
	g_nand_storage_info->RbConnectMode = 0;
	g_nand_storage_info->BankCntPerChip = 1;
	g_nand_storage_info->DieCntPerChip = g_nsi->nci->npi->die_cnt_per_chip;
	g_nand_storage_info->PlaneCntPerDie = 1;
	g_nand_storage_info->SectorCntPerPage = g_nsi->nci->npi->sect_cnt_per_page;
	g_nand_storage_info->PageCntPerPhyBlk = g_nsi->nci->npi->page_cnt_per_blk;
	g_nand_storage_info->BlkCntPerDie = g_nsi->nci->npi->blk_cnt_per_die;
	g_nand_storage_info->OperationOpt = g_nsi->nci->npi->operation_opt;
	g_nand_storage_info->FrequencePar = g_nsi->nci->npi->access_freq;
	g_nand_storage_info->EccMode = g_nsi->nci->npi->ecc_mode;
	g_nand_storage_info->ValidBlkRatio = g_nsi->nci->npi->valid_blk_ratio;
	g_nand_storage_info->ReadRetryType = g_nsi->nci->npi->read_retry_type;
	g_nand_storage_info->DDRType = g_nsi->nci->npi->ddr_type;
	g_nand_storage_info->random_addr_num = g_nsi->nci->npi->random_addr_num;
	g_nand_storage_info->random_cmd2_send_flag = g_nsi->nci->npi->random_cmd2_send_flag;
	g_nand_storage_info->nand_real_page_size = g_nsi->nci->npi->nand_real_page_size;

	memcpy(g_nand_storage_info->NandChipId, g_nsi->nci->id, 8);
	memcpy(&g_nand_storage_info->OptPhyOpPar, g_nsi->nci->opt_phy_op_par, sizeof(struct nand_phy_op_par));

	return 0;
}


/*
 * rawnand_special_init: rawnand special init
 * */
int rawnand_special_init(void)
{
	enum nand_readretry_type type;
	struct nand_chip_info *nci = g_nctri->nci;
	if (nci == NULL) {
		RAWNAND_ERR("%s nci is null\n", __func__);
		return ERR_NO_12;
	}

	type = nci->npi->selected_readretry_no;

	rawnand_chip_special_init(type);

	return 0;
}

/*
 * rawnand_special_exit: rawnand special exit
 * */
int rawnand_special_exit(void)
{
	enum nand_readretry_type type;
	struct nand_chip_info *nci = g_nctri->nci;
	if (nci == NULL) {
		RAWNAND_ERR("%s nci is null\n", __func__);
		return ERR_NO_12;
	}

	type = nci->npi->selected_readretry_no;

	rawnand_chip_special_exit(type);

	return 0;
}
/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
void nand_cfg_setting(void)
{
	g_phy_cfg = &aw_nand_info.nand_cfg;
	aw_nand_info.nand_cfg.phy_interface_cfg = nand_cfg_interface();

	aw_nand_info.nand_cfg.phy_support_two_plane = nand_support_two_plane();
	aw_nand_info.nand_cfg.phy_nand_support_vertical_interleave = nand_support_vertical_interleave();
	aw_nand_info.nand_cfg.phy_support_dual_channel = nand_support_dual_channel();

	aw_nand_info.nand_cfg.phy_wait_rb_before = nand_wait_rb_before();
	aw_nand_info.nand_cfg.phy_wait_rb_mode = nand_wait_rb_mode();
	aw_nand_info.nand_cfg.phy_wait_dma_mode = nand_wait_dma_mode();
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int nand_physic_init(void)
{
#if 0
	RAWNAND_DBG("nand_physic_init\n");

	if (init_parameter() != 0) {
		RAWNAND_ERR("nand_physic_init init_parameter error\n");
		return NAND_OP_FALSE;
	}

	if (nand_build_nsi(g_nsi, g_nctri) != 0) {
		RAWNAND_ERR("nand_physic_init nand_build_nsi error\n");
		return NAND_OP_FALSE;
	}

	storage_type = 1;

	if (check_nctri(g_nctri) != 0) {
		RAWNAND_ERR("nand_physic_init check_nctri error\n");
		return NAND_OP_FALSE;
	}

	set_nand_script_frequence();

	if (update_nctri(g_nctri) != 0) {
		RAWNAND_ERR("nand_physic_init update_nctri error\n");
		return NAND_OP_FALSE;
	}

	/*special_ops.nand_physic_special_init();*/
	rawnand_special_init();

	nand_physic_info_read();

	if (nand_build_nssi(g_nssi, g_nctri) != 0) {
		RAWNAND_ERR("nand_physic_init nand_build_nssi error\n");
		return NAND_OP_FALSE;
	}

	nand_build_storage_info();

	show_static_info();

	nand_code_info();

	//nand_phy_test();
	//nand_phy_erase_all();

	RAWNAND_DBG("nand_physic_init end\n");
#endif
	return NAND_OP_TRUE;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int nand_physic_exit(void)
{
	struct nand_controller_info *nctri = g_nctri;

	RAWNAND_DBG("nand_physic_exit\n");
	if (g_nctri == NULL) {
		RAWNAND_INFO("%s %d no resource to free\n", __func__, __LINE__);
		return 0;
	}

	rawnand_special_exit();

	while (nctri != NULL) {
		NAND_ClkRelease(nctri->channel_id);
		NAND_PIORelease(nctri->channel_id);
		if (nctri->dma_type == DMA_MODE_GENERAL_DMA) {
			NAND_ReleaseDMA(nctri->channel_id);
		}
		nand_free_dma_desc(nctri);
		nctri = nctri->next;
	}

	NAND_ReleaseVoltage();


	nand_permanent_data.magic_data = MAGIC_DATA_FOR_PERMANENT_DATA;
	nand_permanent_data.support_two_plane = 0;
	nand_permanent_data.support_vertical_interleave = 0;
	nand_permanent_data.support_dual_channel = 0;

	nand_exit_temp_buf(&ntf);
	delete_nctri();
	delete_nsi();
	delete_nssi();
	rawnand_delete_storage_info();

	return 0;
}

void init_list_head(void **head)
{
	if (*head != NULL) {
		*head = NULL;
	}
}

static void rawnand_dump_clock(void)
{
	RAWNAND_INFO("---%s start---\n", __func__);

#if defined(CONFIG_MACH_SUN50IW11)
	RAWNAND_INFO("SUNXI_CCM_BASE: 0x%08x\n", SUNXI_CCM_BASE);
	RAWNAND_INFO("nand0 clk: %x\n", readl(SUNXI_CCM_BASE + 0x810));
	RAWNAND_INFO("nand1 clk: %x\n", readl(SUNXI_CCM_BASE + 0x814));
	RAWNAND_INFO("nand bus gating reset: %x\n", readl(SUNXI_CCM_BASE + 0x82c));
	RAWNAND_INFO("mbus cfg: %x\n", readl(SUNXI_CCM_BASE + 0x540));
	RAWNAND_INFO("mbus master clk gating: %x\n", readl(SUNXI_CCM_BASE + 0x804));
	RAWNAND_INFO("SUNXI_PRCM_BASE: 0x%08x\n", SUNXI_PRCM_BASE);
	RAWNAND_INFO("pll_peri ctrl: %x\n", readl(SUNXI_PRCM_BASE + 0x1010));
#elif defined(CONFIG_MACH_SUN50IW10) || defined(CONFIG_MACH_SUN50IW9)
	RAWNAND_INFO("SUNXI_CCM_BASE: 0x%08x\n", SUNXI_CCM_BASE);
	RAWNAND_INFO("nand0 clk: %x\n", readl(SUNXI_CCM_BASE + 0x810));
	RAWNAND_INFO("nand1 clk: %x\n", readl(SUNXI_CCM_BASE + 0x814));
	RAWNAND_INFO("nand bus gating reset: %x\n", readl(SUNXI_CCM_BASE + 0x82c));
	RAWNAND_INFO("mbus cfg: %x\n", readl(SUNXI_CCM_BASE + 0x540));
	RAWNAND_INFO("mbus master clk gating: %x\n", readl(SUNXI_CCM_BASE + 0x804));
	RAWNAND_INFO("pll_peri0 ctrl: %x\n", readl(SUNXI_CCM_BASE + 0x20));
	RAWNAND_INFO("pll_peri1 ctrl: %x\n", readl(SUNXI_CCM_BASE + 0x28));
#endif
	RAWNAND_INFO("---%s end---\n", __func__);
}

static void rawnand_dump_gpio_nand(void)
{
	RAWNAND_INFO("---%s start---\n", __func__);
#if defined(CONFIG_MACH_SUN50IW11)
	RAWNAND_INFO("SUNXI_PIO_BASE: 0x%08x\n", SUNXI_PIO_BASE);
	RAWNAND_INFO("pc cfg0: %x\n", readl(SUNXI_PIO_BASE + 0x48));
	RAWNAND_INFO("pc data: %x\n", readl(SUNXI_PIO_BASE + 0x58));
	RAWNAND_INFO("pc drv_lvl: %x\n", readl(SUNXI_PIO_BASE + 0x5c));
	RAWNAND_INFO("pc pull: %x\n", readl(SUNXI_PIO_BASE + 0x64));

	RAWNAND_INFO("pf cfg0: %x\n", readl(SUNXI_PIO_BASE + 0xb4));
	RAWNAND_INFO("pf data: %x\n", readl(SUNXI_PIO_BASE + 0xc4));
	RAWNAND_INFO("pf drv_lvl: %x\n", readl(SUNXI_PIO_BASE + 0xc8));
	RAWNAND_INFO("pf pull: %x\n", readl(SUNXI_PIO_BASE + 0xd0));
	RAWNAND_INFO("voltage mode select: %x\n", readl(SUNXI_PIO_BASE + 0x340));
#elif defined(CONFIG_MACH_SUN50IW10) || defined(CONFIG_MACH_SUN50IW9)
	RAWNAND_INFO("SUNXI_PIO_BASE: 0x%08x\n", SUNXI_PIO_BASE);
	RAWNAND_INFO("pc cfg0: %x\n", readl(SUNXI_PIO_BASE + 0x48));
	RAWNAND_INFO("pc cfg1: %x\n", readl(SUNXI_PIO_BASE + 0x4c));
	RAWNAND_INFO("pc cfg2 %x\n", readl(SUNXI_PIO_BASE + 0x50));
	RAWNAND_INFO("pc data: %x\n", readl(SUNXI_PIO_BASE + 0x58));
	RAWNAND_INFO("pc drv0_lvl: %x\n", readl(SUNXI_PIO_BASE + 0x5c));
	RAWNAND_INFO("pc drv1_lvl: %x\n", readl(SUNXI_PIO_BASE + 0x60));
	RAWNAND_INFO("pc pull_0: %x\n", readl(SUNXI_PIO_BASE + 0x64));
	RAWNAND_INFO("pc pull_1: %x\n", readl(SUNXI_PIO_BASE + 0x68));
	RAWNAND_INFO("voltage mode select: %x\n", readl(SUNXI_PIO_BASE + 0x340));
#endif
	RAWNAND_INFO("---%s end---\n", __func__);
}
/**
 * rawnand_channel_init: init channel
 * @channel : channel num
 */
static int rawnand_channel_init(int channel)
{
	struct nand_controller_info *nctri = NULL;
	int ret = 0;

	if (channel == 0) {
		/*the nctri list*/
		init_list_head((void **)&g_nctri);

		g_nsi = &g_nsi_data;
		if (g_nsi == NULL) {
			RAWNAND_ERR("rawnand err: %s g_nsi is null\n", __func__);
			return ERR_NO_12;
		}
		memset(g_nsi, 0, sizeof(struct _nand_storage_info));


	}
	nctri = &g_nctri_data[channel];
	if (nctri == NULL) {
		RAWNAND_ERR("%s channel@%d no memory for nctri\n", __func__,
				channel);
		return ERR_NO_12; /*-ENOMEM*/
	}

	memset(nctri, 0, sizeof(struct nand_controller_info));
	add_to_nctri(nctri);
	/*fill the channel's nctri with its register address*/
	nctri->nreg_base = nand_get_channel_base_addr(channel);
	g_nreg_base = nctri->nreg_base;
	fill_nctri(nctri);


	/*init */
	if (init_nctri(nctri)) {
		RAWNAND_ERR("%s channel@%d init nctri fail\n", __func__,
				channel);
		ret = ERR_NO_17;
		goto err0;
	}
	/*get the channel's cpu dma dest addr*/
	nand_get_dma_desc(nctri);

	/*init the channel's chip*/
	ret = rawnand_chips_init((struct nand_chip_info *)&nctri->nci);
	if (ret != NAND_OP_TRUE) {
		RAWNAND_ERR("%s channel@%d chips init fail\n", __func__,
				channel);
		ret = ERR_NO_18;
		goto err0;
	}

	return NAND_OP_TRUE;

err0:
	delete_from_nctri_by_channel(channel);
	return NAND_OP_FALSE;
}
/**
 * rawnand_channel_init_tail: channel init tail
 * eg. some channel's chips special init
 * @channel : channel num
 */
static int rawnand_channel_init_tail(void)
{
	struct nand_chip_info *nci = g_nctri->nci;
	if (nci == NULL) {
		RAWNAND_ERR("%s nci is null\n", __func__);
		return ERR_NO_12;
	}

	g_nssi = &g_nssi_data;
	if (g_nssi == NULL) {
		RAWNAND_ERR("rawnand err: %s g_nssi is null\n", __func__);
		return ERR_NO_12;
	}
	memset(g_nssi, 0, sizeof(struct _nand_super_storage_info));
	set_nand_script_frequence();

	/*according to the id table, update the channel*/
	if (update_nctri(g_nctri) != NAND_OP_TRUE) {
		RAWNAND_ERR("rawnand err: %s update nctri fail\n", __func__);
		return NAND_OP_FALSE;
	}

	/*rawnand some special init. eg. chip special request of readretry*/
	rawnand_special_init();

	nand_physic_info_read();

	/*build the super chips*/
	if (rawnand_sp_chips_init((struct nand_super_chip_info *)&g_nssi->nsci) != NAND_OP_TRUE) {
		RAWNAND_ERR("%s %d rawnand super chips init fail\n",
				__func__, __LINE__);
		return NAND_OP_FALSE;
	}
	return NAND_OP_TRUE;
}
/**
 * rawnand_hw_init: rawnand hardward init
 */
int rawnand_hw_init(void)
{
	int cn = 0;
	int ret = 0;
	struct nand_controller_info *nctri = NULL;
	struct nand_controller_info *nctri_temp = NULL;

	RAWNAND_INFO("enter rawnand hardward init..\n");

	/*request nand temp buffer*/
	nand_init_temp_buf(&ntf);

	/*PC withstand volatage mode switch to 3.3v,consister other module
	 * switch it to 1.8v, if nand need 1.8v, configure id table ddr_opt
	 * bit16(NAND_VCCQ_1P8V), then update the withstand volatage mode*/
	nand_enable_vccq_3p3v();

	/*init channel*/
	for (cn = 0; cn < MAX_CHANNEL; cn++) {

		ret = rawnand_channel_init(cn);
		if (ret != NAND_OP_TRUE) {
			if (cn >= 1)
				goto right;
			else
				goto err0;
		}

		/**
		 * the connect info of different channel should keep the same
		 */
		if (cn >= 1) {
			nctri_temp = nctri_get(g_nctri, cn - 1);
			if (nctri_temp == NULL) {
				RAWNAND_ERR("%s channel@%d is null\n", __func__,
						cn - 1);
				goto err0;
			}

			nctri = nctri_get(g_nctri, cn);
			if (nctri == NULL) {
				RAWNAND_ERR("%s channel@%d is null\n", __func__,
						cn);
				goto err0;
			}
			if (nctri_temp->chip_connect_info !=
					nctri->chip_connect_info) {
				RAWNAND_ERR("%s channel@%d connect info is"
						"different with channel@%d\n",
						__func__, cn - 1, cn);
				goto err0;
			}
		}
	} /*MAX_CHANNEL*/

	if (rawnand_channel_init_tail() != NAND_OP_TRUE) {
		RAWNAND_ERR("%s rawnand channel tail init fail\n", __func__);
		goto err0;
	}

	if (rawnand_storage_init() != NAND_OP_TRUE) {
		RAWNAND_ERR("%s rawnand storage init fail\n", __func__);
		goto err0;
	}

	/*print the channel' info*/
	show_static_info();

right:
	RAWNAND_INFO("exit rawnand hardward init\n");
	return NAND_OP_TRUE;

err0:
	RAWNAND_INFO("rawnand hardward init fail\n");
	delete_nctri();
	return NAND_OP_FALSE ;
}
void rawnand_set_nand_info_data(struct _nand_info *nand_info)
{
	nand_info->type = 0;
	nand_info->SectorNumsPerPage = g_nssi->nsci->sector_cnt_per_super_page;
	nand_info->BytesUserData = g_nssi->nsci->spare_bytes;
	nand_info->BlkPerChip = g_nssi->nsci->blk_cnt_per_super_chip;

	nand_info->ChipNum = g_nssi->super_chip_cnt;

	nand_info->PageNumsPerBlk = g_nssi->nsci->page_cnt_per_super_blk;

	nand_info->MaxBlkEraseTimes = g_nssi->nsci->nci_first->max_erase_times;

	nand_info->EnableReadReclaim = 1;

	nand_info->FullBitmap = g_nssi->nsci->sector_cnt_per_super_page;

	nand_info->boot = phyinfo_buf;

	/*aw_nand_info.MaxBlkEraseTimes = 2000;*/
	/*aw_nand_info.EnableReadReclaim = (g_nsi->nci->npi->operation_opt & NAND_READ_RECLAIM) ? 1 : 0;*/

	if (aw_nand_info.boot->physic_block_reserved == 0)
		aw_nand_info.boot->physic_block_reserved = PHYSIC_RECV_BLOCK;

	/*set aw_nand_info.boot->uboot_start_block
	 *    aw_nand_info.boot->uboot_next_block*/
	set_uboot_start_and_end_block();
}
/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
struct _nand_info *RawNandHwInit(void)
{
	int ret;

	if (nand_open_count) {
		RAWNAND_INFO("%s nand had opend\n", __func__);
		return &aw_nand_info;
	}

	nand_cfg_setting();


	/*ret = nand_physic_init();*/
	ret = rawnand_hw_init();
	if (ret != 0) {
		rawnand_dump_clock();
		rawnand_dump_gpio_nand();
		RAWNAND_ERR("nand_physic_init error %d\n", ret);
		return NULL;
	}

	rawnand_set_nand_info_data(&aw_nand_info);

	set_hynix_special_info();

	nand_secure_storage_init(0);

	nand_open_count++;

	RAWNAND_DBG("RawNandHwInit end\n");

	return &aw_nand_info;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int RawNandHwExit(void)
{
	if (nand_open_count) {
		RAWNAND_INFO("%s nand not need to close!\n", __func__);
		return 0;
	}
	NAND_PhysicLock();
	nand_wait_all_rb_ready();
	nand_physic_exit();
	NAND_PhysicUnLock();
	return 0;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int RawNandHwSuperStandby(void)
{
	struct nand_controller_info *nctri = g_nctri;

	RAWNAND_DBG("RawNandHwSuperStandby start\n");
	NAND_PhysicLock();
	nand_wait_all_rb_ready();

	while (nctri != NULL) {
		save_nctri(nctri);
		//show_nctri(nctri);
		//show_nci(nctri->nci);
		switch_ddrtype_from_ddr_to_sdr(nctri);
		NAND_ClkRelease(nctri->channel_id);
		NAND_PIORelease(nctri->channel_id);
		nctri = nctri->next;
	}

	NAND_ReleaseVoltage();

	return 0;
}
/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int RawNandHwSuperResume(void)
{
	struct nand_controller_info *nctri;
	struct nand_chip_info *nci;
	//	u8 spare[16];
	//	u8* mbuf;
	//	u32 t;

	RAWNAND_DBG("RawNandHwSuperResume start\n");
	NAND_GetVoltage();
	nctri = g_nctri;
	while (nctri != NULL) {
		NAND_PIORequest(nctri->channel_id);
		NAND_ClkRequest(nctri->channel_id);
		ndfc_soft_reset(nctri);
		recover_nctri(nctri);

		nci = nctri->nci;
		while (nci != NULL) {
			nand_reset_chip(nci);
			nci = nci->nctri_next;
		}
		nctri = nctri->next;
	}

	nctri = g_nctri;
	while (nctri != NULL) {
		update_nctri(nctri);
		//show_nctri(nctri);
		//show_nci(nctri->nci);
		nctri = nctri->next;
	}

	//////////////////////////////////////////
	//    nctri = g_nctri;
	//    while(nctri != NULL)
	//    {
	//        nci = nctri->nci;
	//        while(nci != NULL)
	//        {
	//            nand_reset_chip(nctri->nci);
	//            nci = nci->nctri_next;
	//        }
	//        nctri = nctri->next;
	//    }
	//////////////////////////////////////////
	//    if(g_nsi->nci->driver_no == 6)
	//    {
	//        nand_physic_read_page(0,7,2,g_nsi->nci->sector_cnt_per_page,NULL,spare);
	//    }

	nand_wait_all_rb_ready();
	NAND_PhysicUnLock();

	//    if(g_nsi->nci->driver_no == 6)
	//    {
	//        RAWNAND_DBG("=======================\n");
	//        t = 0xfffff;
	//        while(--t);
	//        mbuf = nand_get_temp_buf(32*1024);
	//        nand_physic_read_page(0,7,2,g_nsi->nci->sector_cnt_per_page,mbuf,spare);
	//        nand_free_temp_buf(mbuf,32*1024);
	//    }

	RAWNAND_DBG("RawNandHwSuperResume end\n");
	return 0;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int RawNandHwNormalStandby(void)
{
	RAWNAND_DBG("RawNandHwNormalStandby start\n");
	NAND_PhysicLock();
	nand_wait_all_rb_ready();
	return 0;
}
/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int RawNandHwNormalResume(void)
{
	RAWNAND_DBG("RawNandHwNormalResume start\n");
	NAND_PhysicUnLock();
	return 0;
}
/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int RawNandHwShutDown(void)
{
	RAWNAND_DBG("RawNandHwShutDown start\n");
	NAND_PhysicLock();
	nand_physic_exit();
	nand_wait_all_rb_ready();
	return 0;
}
